﻿using System;
using System.Collections.Generic;
using System.Configuration;
using FunctionalCLR.Core.Function.Flow;
using FunctionalCLR.Core.Functions.Maths;
using FunctionalCLR.Core.Function;

namespace FunctionalCLR.Core {
    public class DefaultFunctionManager : IFunctionManager {

        private Dictionary<string, object> functions 
            = new Dictionary<string, object>();

        public DefaultFunctionManager() {
            DefaultFunctionLoader dfl = new DefaultFunctionLoader(this);
            dfl.Load(Utils.FunctionPath);
        }

        #region IFunctionManager Members

        public virtual List<FuncNode> GetAll() {
            return null;
        }

        public virtual void Add(string funcName, object func) {
            if (this.functions.ContainsKey(funcName)) {
                return;
            }

            this.functions.Add(funcName, func);
        }

        public virtual FuncNode Get(string functionName) {
            if (functions.ContainsKey(functionName)) {
                object obj = functions[functionName];
                if (obj is Type) {
                    Type type = obj as Type;
                    return (FuncNode)
                        type.Assembly.CreateInstance(type.FullName);
                } else {
                    return Clone((UserDefinedFunctionNode)obj);
                }
            }

            switch (functionName) {
                case "sin":
                    return new SinNode();
                case "cos":
                    return new CosNode();
                case "asin":
                    return new AsinNode();
                case "acos":
                    return new AcosNode();
                case "tan":
                    return new TanNode();
                case "atan":
                    return new AtanNode();
                case "tanh":
                    return new TanhNode();
                case "atanh":
                    return new AtanhNode();
                case "abs":
                    return new AbsNode();
                case "sign":
                    return new SignNode();
                case "floor":
                    return new FloorNode();
                case "ceil":
                    return new CeilingNode();
                case "max":
                    return new MaxNode();
                case "if":
                    return new IfNode();
                case "asinh":
                    return new AsinhNode();
                case "acosh":
                    return new AcoshNode();
                case "sinh":
                    return new SinhNode();
                case "cosh":
                    return new CoshNode();
                default:
                    string config = ConfigurationManager.AppSettings[functionName.ToLower()];
                    if (string.IsNullOrEmpty(config)) {
                        return null;
                    }
                    string assemblyName = config.Split(',')[0].Trim();
                    string typeName = config.Split(',')[1].Trim();

                    FuncNode node
                        = (FuncNode)Utils.GetObject(assemblyName, typeName);
                    return node;
            }
        }

        public UserDefinedFunctionNode Clone(UserDefinedFunctionNode funcNode) {
            UserDefinedFunctionNode newCopy = new UserDefinedFunctionNode();
            UserDefinedFunctionNode template = funcNode as UserDefinedFunctionNode;

            newCopy.Name = template.Name;
            newCopy.Parameters = new string[template.Parameters.Length];
            for (int index = 0; index < template.Parameters.Length; index++) {
                newCopy.Parameters[index] = template.Parameters[index];
            }
            newCopy.Precedence = template.Precedence;
            newCopy.LineNumber = template.LineNumber;
            newCopy.Value = template.Value;
            newCopy.Comment = template.Comment;
            newCopy.ClassName = template.ClassName;
            newCopy.Body = template.Body;
            newCopy.FuncBody = template.FuncBody;
            newCopy.ParserFactory = template.ParserFactory;
            newCopy.FunctionManager = this;

            return newCopy;
        }

        public virtual void Remove(string funcName) {
            this.functions.Remove(funcName);
        }

        public virtual bool Exist(string fileName) {
            return this.functions.ContainsKey(fileName);
        }

        #endregion
        
        /// <summary>
        /// Check if the function is defined by user.
        /// </summary>
        /// <param name="funcName"></param>
        /// <returns></returns>
        public bool IsUserDefinedFunction(string funcName) {
            return !this.functions.ContainsKey(funcName);
        }

    }
}
